package com.cloudptt.api.pjsua2;

import android.content.Context;
import android.content.Intent;
import android.view.Surface;

import com.cloudptt.api.comm.CauseCont;
import com.cloudptt.api.models.call.CallExtStrInfo;
import com.cloudptt.api.models.call.RtcpMsg;
import com.cloudptt.api.pjsip.api.SipCallSession;
import com.cloudptt.api.pjsip.api.SipManager;
import com.cloudptt.api.product.func.call.CallAttr;
import com.cloudptt.api.product.log.Log;
import com.mcptt.db.model.AddrInfo;

import org.pjsip.pjsua2.AudioMedia;
import org.pjsip.pjsua2.Call;
import org.pjsip.pjsua2.CallInfo;
import org.pjsip.pjsua2.CallMediaInfo;
import org.pjsip.pjsua2.CallMediaInfoVector;
import org.pjsip.pjsua2.CallOpParam;
import org.pjsip.pjsua2.Media;
import org.pjsip.pjsua2.OnCallMediaStateParam;
import org.pjsip.pjsua2.OnCallStateParam;
import org.pjsip.pjsua2.OnFloorInfoParam;
import org.pjsip.pjsua2.SipHeader;
import org.pjsip.pjsua2.SipHeaderVector;
import org.pjsip.pjsua2.VideoPreview;
import org.pjsip.pjsua2.VideoPreviewOpParam;
import org.pjsip.pjsua2.VideoWindow;
import org.pjsip.pjsua2.VideoWindowHandle;
import org.pjsip.pjsua2.pjmedia_orient;
import org.pjsip.pjsua2.pjmedia_type;
import org.pjsip.pjsua2.pjsip_inv_state;
import org.pjsip.pjsua2.pjsip_status_code;
import org.pjsip.pjsua2.pjsua2;
import org.pjsip.pjsua2.pjsua_call_media_status;

import java.util.List;

public class MyCall extends Call {
    public static String TAG = "MyCall";
    public static String EXT_KEY = "CallInfo";
    public static String DEF_CALL_ID_STR = "0-0-0-0";
    private Context ctx = null;
    private String callIdStr = DEF_CALL_ID_STR;
    private int callType = CallAttr.CallType_Single;
    private int callDir = CallAttr.CallDir_MO;
    public VideoWindow vidWin;
    public VideoPreview vidPrev;
    private SipCallSession session = null;

    private int curCallStatus = pjsip_inv_state.PJSIP_INV_STATE_NULL;

    private SipHeaderVector vec = null;
    private SipHeader header = null;
    private CallOpParam prm = null;
    private CallExtStrInfo info = null;
    //    private String infoStr = null;
    private String buddy_uri = null;

    private CallOpParam outPrm = null;

    private VideoPreviewOpParam vidPrevParam = null;
    private VideoWindowHandle vidWH = null;
    private Surface localSufaceView = null;
    private Surface remoteSufaceView = null;

//    private CallOpParam outPrm200 = null;
    private MyAccount acc = null;


//    private int prio = 1;
    private static List<String> idList = null;
    private String allCallGNum = "";
    private String curGroupNum = "";
    private String sosGroupNum = "";

    private String selfNum = "";
    private float txLevel = 0;
    private float rxLevel = 0;





    public MyCall(Context ctx, MyAccount acc, int call_id,String allGroupNum,String curGroupNum,float txLevel,float rxLevel)
    {
        super(acc, call_id);
        this.ctx = ctx;
        this.acc = acc;
        this.callType = callType;
        this.callDir = callDir;
        this.allCallGNum = allGroupNum;
        this.curGroupNum = curGroupNum;
        vidWin = null;
        this.txLevel = txLevel;
        this.rxLevel = rxLevel;
    }

    public void updateCallInfo(String callIdStr,int callType,int callDir)
    {
        this.callIdStr = callIdStr;
        this.callType = callType;
        this.callDir = callDir;
    }
    public void setCallGNum(String allCallGNum,String curGroupNum,String sosGroupNum,String selfNum) {
        Log.d(TAG, "setAllCallGNum  allCallGNum = " + allCallGNum +" curGroupNum = " + curGroupNum +" sosGroupNum = " + sosGroupNum +" callType = " + callType+" selfNum = " + selfNum);
        if(callType == CallAttr.CallType_Group) {
        this.allCallGNum = allCallGNum;
        this.curGroupNum = curGroupNum;
        this.sosGroupNum = sosGroupNum;
        this.selfNum = selfNum;
        try {
            setCurGroup(1, allCallGNum,curGroupNum,sosGroupNum);
            } catch (Exception e) {
            e.printStackTrace();
            }
        }
    }
//    public void setAllCallGNum(String allCallGNum) {
//        this.allCallGNum = allCallGNum;
//    }

    public int makeCall(final String callee,final String caller,final int callType,final boolean isHaveVideo) throws Exception
    {
        prm = new CallOpParam(true);
        if(isHaveVideo)
        {
            prm.getOpt().setVideoCount(1);
        }
        else {
            prm.getOpt().setVideoCount(0);
        }
        
        session = new SipCallSession();
        session.setCallType(callType);
        session.setUseVideo(isHaveVideo);
        session.setRemoteContact(callee);
        session.setLastReasonCode(0);
        session.setCallState(pjsip_inv_state.PJSIP_INV_STATE_CALLING);

        info = new CallExtStrInfo(callType,caller,callee,isHaveVideo);
        if(info != null)
        {
            vec = new SipHeaderVector();
            header = new SipHeader();
            header.setHName(EXT_KEY);
            String infoStr = info.toString();
            header.setHValue(infoStr);
            vec.add(header);
            prm.getTxOption().setHeaders(vec);
        }

        buddy_uri = AddrInfo.toSipUrlFromNum(callee);////acc.rewriteNumber(callee);
        makeCall(buddy_uri, prm);
        return CauseCont.OperateCheck_Suc;
    }

    public int answer(int code,boolean hasVideo) {
        outPrm = new CallOpParam();
        if(code == 180)
        {
            outPrm.setStatusCode(pjsip_status_code.PJSIP_SC_RINGING);
        }
        else {
            outPrm.setStatusCode(pjsip_status_code.PJSIP_SC_OK);
        }

        outPrm.getOpt().setAudioCount(1);
        Log.d(TAG, "hasVideo = " + hasVideo);
        if (hasVideo) {
            outPrm.getOpt().setVideoCount(1);
        } else {
            outPrm.getOpt().setVideoCount(0);
        }
        //prm.getOpt().setVideoCount(1);
        //Log.d(TAG,"video count="+prm.getOpt().getVideoCount()+"; audio count="+prm.getOpt().getAudioCount());
        try {
            answer(outPrm);
            return CauseCont.OperateCheck_Suc;
        } catch (Exception e) {
            e.printStackTrace();
            return CauseCont.OperateErr_Exception;
        }
    }

    public int destoryCurCall() {
        try {
            destoryCall();
            return CauseCont.OperateCheck_Suc;
        } catch (Exception e) {
            e.printStackTrace();
            return CauseCont.OperateErr_Exception;
        }
    }

    public int hangup(int code) {
        outPrm = new CallOpParam();
        outPrm.setStatusCode(code);
        try {
            hangup(outPrm);
            return CauseCont.OperateCheck_Suc;
        } catch (Exception e) {
            e.printStackTrace();
            return CauseCont.OperateErr_Exception;
        }
    }

    @Override
    public void onCallState(OnCallStateParam prm) {
        Log.d(TAG, "onCallState prm = " + prm);
        CallInfo info = null;
        try {
            info = getInfo();
            Log.d(TAG, "onCallState info state:" + info.getState());
            curCallStatus = info.getState();
            if (info.getState() == pjsip_inv_state.PJSIP_INV_STATE_CALLING) {
                Log.d(TAG, "onCallState acc = " + acc);
                if(acc != null)
                {
                    String callIdStr = info.getCallIdString();
                    Log.d(TAG, "onCallState callIdStr = " + callIdStr);
                    acc.addMyCall(callIdStr,this);
                }
            }
            if(info.getRemVideoCount() > 0) {
                if (info.getState() == pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED) {
                    MyApp.ep.utilLogWrite(3, "MyCall", dump(true, ""));
                } else if (info.getState() < pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED) {
                    this.opVideoMedia(0);
                } else if (info.getState() == pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED) {
                    this.negoVideoRtp();
                    this.opVideoMedia(1);
                }
            }
        } catch (Exception e) {
        }


        if (info != null) {
            try {
                if (session != null) {
                    session.updateCallInfo(info);
                }
                Log.d(TAG, "onCallState session = " + session);
                int callType = session.getCallType();
                if (callType != CallAttr.CallType_Group) {
                    Intent intent = new Intent(SipManager.ACTION_SINGLE_CALL_INFO);
                    intent.putExtra(SipManager.EXTRA_CALL_INFO, session);
                    ctx.sendBroadcast(intent, SipManager.PERMISSION_USE_SIP);
                } else {
                    Intent intent = new Intent(SipManager.ACTION_SIP_CALL_CHANGED);
                    intent.putExtra(SipManager.EXTRA_CALL_INFO, session);
                    ctx.sendBroadcast(intent, SipManager.PERMISSION_USE_SIP);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // Should not delete this call instance (self) in this context,
        // so the observer should manage this call instance deletion
        // out of this callback context.
//        MyApp.observer.notifyCallState(this);
    }

    @Override
    public void onCallMediaState(OnCallMediaStateParam prm) {
        Log.d(TAG, "onCallMediaState prm = " + prm);
        CallInfo ci;
        try {
            ci = getInfo();
        } catch (Exception e) {
            return;
        }

        CallMediaInfoVector cmiv = ci.getMedia();

        for (int i = 0; i < cmiv.size(); i++) {
            CallMediaInfo cmi = cmiv.get(i);
            Log.d(TAG, "onCallMediaState callMediaInfo type:" + cmi.getType() + "; status:" + cmi.getStatus() + "; IncomingWindowId:" + cmi.getVideoIncomingWindowId());
            if (cmi.getType() == pjmedia_type.PJMEDIA_TYPE_AUDIO &&
                    (cmi.getStatus() == pjsua_call_media_status.PJSUA_CALL_MEDIA_ACTIVE || cmi.getStatus() == pjsua_call_media_status.PJSUA_CALL_MEDIA_REMOTE_HOLD)) {
                // unfortunately, on Java too, the returned Media cannot be
                // downcasted to AudioMedia
                Log.d(TAG, "onCallMediaState audio is ok");
                Media m = getMedia(i);
                AudioMedia am = AudioMedia.typecastFromMedia(m);

                // connect ports
                try {
                    Log.d(TAG, "onCallMediaState rxLevel = " + rxLevel + " txLevel = " + txLevel);
                    am.adjustRxLevel(rxLevel);
                    am.adjustTxLevel(txLevel);
                    //AudioMedia am = getAudioMedia(i);
                    MyApp.ep.audDevManager().getCaptureDevMedia().
                            startTransmit(am);
                    am.startTransmit(MyApp.ep.audDevManager().
                            getPlaybackDevMedia());

                } catch (Exception e) {
                    Log.d(TAG, "onCallMediaState Failed connecting media ports" + e.getMessage());
                    continue;
                }
            } else if (cmi.getType() == pjmedia_type.PJMEDIA_TYPE_VIDEO &&
                    cmi.getStatus() == pjsua_call_media_status.PJSUA_CALL_MEDIA_ACTIVE && cmi.getVideoIncomingWindowId() != pjsua2.INVALID_ID) {
                Log.d(TAG, "onCallMediaState video is ok");
                vidWin = new VideoWindow(cmi.getVideoIncomingWindowId());
                vidPrev = new VideoPreview(cmi.getVideoCapDev());
                try {
                    MyApp.ep.vidDevManager().setCaptureOrient(cmi.getVideoCapDev(), pjmedia_orient.PJMEDIA_ORIENT_ROTATE_270DEG,
                            true);
                }catch (Exception e)
                {}

                Log.d(TAG, "onCallMediaState media active, vidWin = "+vidWin+"; vidPrev = "+vidPrev);

                Intent intent = new Intent(SipManager.ACTION_SIP_MEDIA_CHANGED);
                intent.putExtra(SipManager.EXTRA_MEDIA_STATUS, cmi.getStatus());
                String key = session.getRemoteContact();
                intent.putExtra(SipManager.EXTRA_CALL_KEY, key);
                ctx.sendBroadcast(intent, SipManager.PERMISSION_USE_SIP);
                Log.d(TAG, "onCallMediaState media active, callType = " + callType +"; callDir = " + callDir);
//                if(this.callType == CallAttr.CallType_Video_Push)
//                {
//                    try {
//                        if(this.callDir == CallAttr.CallDir_MT)
//                        {
//                            this.opVideoMedia(1);
//                        }
//                        else if(this.callDir == CallAttr.CallDir_MO)
//                        {
//                            this.opVideoMedia(0);
//                        }
//                    }
//                    catch (Exception e)
//                    {
//                        e.printStackTrace();
//                    }
//
//                }
//                else if(this.callType == CallAttr.CallType_Video_Pull)
//                {
//                    try {
//                        if(this.callDir == CallAttr.CallDir_MO)
//                        {
//                            this.opVideoMedia(0);
//                        }
//                        else if(this.callDir == CallAttr.CallDir_MT)
//                        {
//                            this.opVideoMedia(1);
//                        }
//                    }
//                    catch (Exception e)
//                    {
//                        e.printStackTrace();
//                    }
//                }
//                Intent intent = new Intent("onCallMediaState");
//                //intent.putExtra(SipManager.EXTRA_CALL_INFO, session);
//                ctx.sendBroadcast(intent);
            }
        }

//        MyApp.observer.notifyCallMediaState(this);
    }
    public boolean isVidOk()
    {
        Log.d(TAG, "isVidOk");
        if (vidWin != null &&
                vidPrev != null)
        {
            return true;
        }
        return false;
    }
    public void startLocalSurfaceView(Surface surface) throws Exception
    {
        Log.d(TAG, "startLocalSurfaceView");
        if (vidPrev != null) {
            Log.d(TAG, "startLocalSurfaceView 1");
            vidWH = new VideoWindowHandle();
            Log.d(TAG, "startLocalSurfaceView 2");
            vidWH.getHandle().setWindow(surface);
            Log.d(TAG, "startLocalSurfaceView 3");
            vidPrevParam = new VideoPreviewOpParam();
            Log.d(TAG, "startLocalSurfaceView 4");
            vidPrevParam.setWindow(vidWH);
            Log.d(TAG, "startLocalSurfaceView 5");
            vidPrev.start(vidPrevParam);
            Log.d(TAG, "startLocalSurfaceView 6");
        }
    }
    public void stopLocalSurfaceView() throws Exception
    {
        Log.d(TAG, "stopLocalSurfaceView");
        Log.d(TAG, "stopLocalSurfaceView info state:" + curCallStatus);
        if(curCallStatus == pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED)
        {
//            return;
        }

        if (vidPrev != null) {
            Log.d(TAG, "stopLocalSurfaceView 1");
            vidPrev.stop();
            vidPrev.delete();
            vidPrev = null;
            Log.d(TAG, "stopLocalSurfaceView 2");
        }
    }
    public void setRemoSurfaceView(Surface surface)
    {
        Log.d(TAG, "setRemoSurfaceView info state:" + curCallStatus);
        if(curCallStatus == pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED)
        {
            return;
        }
        Log.d(TAG, "setRemoSurfaceView");
        vidWH = new VideoWindowHandle();
        Log.d(TAG, "setRemoSurfaceView 1");
        try {
//                receiver.getCurrentCall().vidWin.setWindow(vidWH);
            Log.d(TAG, "setRemoSurfaceView 2");
            vidWH.getHandle().setWindow(surface);
            Log.d(TAG, "setRemoSurfaceView 3");
            vidWin.setWindow(vidWH);
//            vidWin.rotate(1);
            //vidWin.setSize(MediaSize);
//            vidWin.setFullScreen(true);
            Log.d(TAG, "setRemoSurfaceView 4");
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    private boolean isAllCurSame()
    {
        Log.d(TAG, "isAllCurSame  curGroupNum = " + curGroupNum + " allCallGNum = " + allCallGNum);
        if(this.allCallGNum != null
            && this.curGroupNum != null
            && this.allCallGNum.equals(curGroupNum))
        {
            return true;
        }
        return false;
    }

    private boolean isSosCurSame()
    {
        Log.d(TAG, "isSosCurSame  curGroupNum = " + curGroupNum + " allCallGNum = " + allCallGNum);
        if(this.sosGroupNum != null
                && this.curGroupNum != null
                && this.sosGroupNum.equals(curGroupNum))
        {
            return true;
        }
        return false;
    }
    private boolean isSameToAllOrCurOrSosOrSelf(String callType,String fromNum,String toNum)
    {
        Log.d(TAG, "isSameToAllOrCurOrSosOrSelf callType =" + callType + " fromNum =" + fromNum + " toNum = " + toNum + " curGroupNum = " + curGroupNum + " allCallGNum = " + allCallGNum + " sosGroupNum = " + sosGroupNum + " selfNum = " + selfNum);
        if(RtcpMsg.isGroupCall(callType)) {
            if (this.allCallGNum != null
                    && this.allCallGNum.equals(toNum)) {
                return true;
            } else if (this.curGroupNum != null
                    && this.curGroupNum.equals(toNum)) {
                return true;
            } else if (this.sosGroupNum != null
                    && this.sosGroupNum.equals(toNum)) {
                return true;
            }
        }
        else
        {
            if (this.selfNum != null
                    && this.selfNum.equals(fromNum)) {
                return true;
            }
            else if (this.selfNum != null
                    && this.selfNum.equals(toNum)) {
                return true;
            }
        }
        return false;
    }
    public void onFloorEvent(OnFloorInfoParam prm)
    {
        Log.d(TAG, "onFloorEvent prm = " + prm);
        if(prm != null)
        {
            String floorInfo = prm.getRtcpMsg();
            Log.d(TAG, "onFloorEvent floorInfo = " + floorInfo);
            String callIdStr = DEF_CALL_ID_STR;
            CallInfo info = null;
            try {
                info = getInfo();
                callIdStr = info.getCallIdString();
            }
            catch (Exception e)
            {
                e.printStackTrace();
                return;
            }

            RtcpMsg rtcpMsg = new RtcpMsg(floorInfo);
//            boolean isSet = false;
            boolean isDiscard = true;
            boolean isAllCurSame = isAllCurSame();
            boolean isSosCurSame = isSosCurSame();
            Log.d(TAG, "onFloorEvent isAllCurSame = " + isAllCurSame + " isSosCurSame = " + isSosCurSame);
            if(isAllCurSame || isSosCurSame)
            {
                isDiscard = false;
            }
            if(isDiscard
                && (rtcpMsg.isGranted() || rtcpMsg.isTaken()
                || rtcpMsg.isIdle() || rtcpMsg.isDeny()))
            {
                String toNum = rtcpMsg.getValue(RtcpMsg.Key_To);
                String fromNum = rtcpMsg.getValue(RtcpMsg.Key_From);
                String callType = rtcpMsg.getValue(RtcpMsg.Key_CallType);

                Log.d(TAG, "onFloorEvent isTaken callType = " + callType +" fromNum = " + toNum+" toNum = " + fromNum + " curGroupNum = " + curGroupNum + " allCallGNum = " + allCallGNum);

                if(isDiscard)
                {
                    boolean isSameToAllOrCurOrSosOrSelf = isSameToAllOrCurOrSosOrSelf(callType,fromNum,toNum);
                    Log.d(TAG, "onFloorEvent isSameToAllOrCurOrSosOrSelf = " + isSameToAllOrCurOrSosOrSelf);
                    if(isSameToAllOrCurOrSosOrSelf)
                    {
                        isDiscard = false;
                    }
                }

                if(!isDiscard
                        && rtcpMsg.isTaken()) {
                    try {
                        if(RtcpMsg.isGroupCall(callType)) {
                            Log.d(TAG, "onFloorEvent allCallGNum = " + allCallGNum + " curGroupNum = " + curGroupNum);
                            this.setCurGroup(1, allCallGNum, curGroupNum, sosGroupNum);
                        }
                        else {
                            if(selfNum != null && selfNum.equals(fromNum))
                            {
                                Log.d(TAG, "onFloorEvent allCallGNum = " + allCallGNum + " toNum = " + toNum);
                                this.setCurGroup(1, allCallGNum, toNum, sosGroupNum);
                            }
                            else if(selfNum != null && selfNum.equals(toNum))
                            {
                                Log.d(TAG, "onFloorEvent allCallGNum = " + allCallGNum + " fromNum = " + fromNum);
                                this.setCurGroup(1, allCallGNum, fromNum, sosGroupNum);
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }

            Log.d(TAG, "onFloorEvent isDiscard = " + isDiscard);
            if(isDiscard)
            {
                return;
            }
            Intent callStateChangedIntent = new Intent(
                    SipManager.ACTION_SIP_SPEAKING_CHANGED);
            callStateChangedIntent.putExtra(SipManager.EXTRA_CALL_ID, callIdStr);
            callStateChangedIntent.putExtra(SipManager.EXTRA_SPEAKING_INFO, rtcpMsg);
            ctx.sendBroadcast(callStateChangedIntent,
                    SipManager.PERMISSION_USE_SIP);
        }
    }

    public void clearCurGroup()
    {

//        try {
////            curGroupNum = "";
//            prio = 0;
//            Log.d(TAG, "clearCurGroup prio = " + prio + " groupNum = " + curGroupNum + " allCallGNum = " + allCallGNum);
//            this.setCurGroup(prio,allCallGNum, curGroupNum);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
    }

    public SipCallSession getSession() {
        return session;
    }

    public int getCallType() {
        return callType;
    }

    public void setSession(SipCallSession session) {
        this.session = session;
    }

    public static void setIdList(List<String> idList) {
        MyCall.idList = idList;
    }
}
